package org.example;

import java.util.Stack;

public class RPNGenerator {

    private Lexer lexer;  // 词法分析器，用于分解输入的表达式
    private String currentToken;  // 当前的 token（符号）
    private StringBuilder processLog;  // 记录操作符栈变换和 RPN 的生成过程
    private int stepCount;  // 步骤计数器，用于记录步骤

    // 构造函数，初始化 RPN 生成器，传入表达式
    public RPNGenerator(String expression) {
        this.lexer = new Lexer(expression);  // 用传入的表达式创建 Lexer 实例
        this.currentToken = lexer.nextToken();  // 获取表达式中的第一个 token
        this.processLog = new StringBuilder();  // 初始化过程日志
        this.stepCount = 1;  // 初始化步骤计数器
    }

    // 生成后缀表达式（逆波兰表示法）
    public String generateRPN() throws Exception {
        Stack<String> operators = new Stack<>();  // 操作符栈
        StringBuilder rpn = new StringBuilder();  // 存储最终的后缀表达式
        // 初始化日志
        processLog.append(String.format("%-5s%-20s%-20s%-30s\n", "步骤", "扫描元素", "符号栈", "结果栈"));
        // 遍历所有的 token
        while (currentToken != null) {
            // 记录当前步骤
            String operatorStack = operators.toString();
            String resultStack = rpn.toString();
            // 如果是数字，将其直接添加到结果字符串中（后缀表达式中数字是按顺序输出的）
            if (isNumber(currentToken)) {
                rpn.append(currentToken).append(" ");  // 数字后面加一个空格
                logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                eat(currentToken);  // 处理当前的数字，继续解析下一个 token
            }
            // 如果是左括号，直接入栈
            else if (currentToken.equals("(")) {
                operators.push(currentToken);  // 左括号是操作符，入栈
                logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                eat("(");  // 处理左括号，继续解析下一个 token
            }
            // 如果是右括号，弹出栈中的操作符直到遇到左括号
            else if (currentToken.equals(")")) {
                // 处理右括号，弹出所有栈中的操作符并添加到结果中，直到遇到左括号
                while (!operators.isEmpty() && !operators.peek().equals("(")) {
                    rpn.append(operators.pop()).append(" ");  // 弹出栈顶操作符
                    logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                }
                if (operators.isEmpty()) {
                    throw new Exception("没有匹配的左括号");  // 如果没有匹配的左括号，抛出异常
                }
                operators.pop();  // 弹出左括号，丢弃
                logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                eat(")");  // 处理右括号，继续解析下一个 token
            }
            // 如果是操作符，则根据优先级进行处理
            else if (isOperator(currentToken)) {
                // 当前操作符和栈顶的操作符进行优先级比较，若当前操作符优先级较低，栈顶操作符出栈
                while (!operators.isEmpty() && hasPrecedence(currentToken, operators.peek())) {
                    rpn.append(operators.pop()).append(" ");  // 弹出栈顶操作符并添加到结果中
                    logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                }
                operators.push(currentToken);  // 当前操作符入栈
                logStep(currentToken, operatorStack, resultStack);  // 记录当前步骤
                eat(currentToken);  // 处理当前操作符，继续解析下一个 token
            }
        }

        // 最后将栈中剩余的操作符全部弹出并添加到结果中
        while (!operators.isEmpty()) {
            rpn.append(operators.pop()).append(" ");
            logStep(currentToken, operators.toString(), rpn.toString());  // 记录当前步骤
        }

        return rpn.toString().trim();  // 返回最终的后缀表达式，去掉尾部多余的空格
    }

    // 记录当前步骤
    private void logStep(String currentToken, String operatorStack, String resultStack) {
        processLog.append(String.format("%-10d%-20s%-20s%-20s\n", stepCount++, currentToken, operatorStack, resultStack));
    }

    // 获取过程
    public String getProcessLog() {
        return processLog.toString();
    }

    // 处理当前 token，若当前 token 与传入的 token 相等，则继续解析下一个 token
    private void eat(String token) {
        if (currentToken != null && currentToken.equals(token)) {
            currentToken = lexer.nextToken();  // 获取下一个 token
        } else {
            throw new RuntimeException("错误字符: " + token + " 目前字符: " + currentToken);
        }
    }

    // 判断当前 token 是否为数字
    private boolean isNumber(String token) {
        try {
            Double.parseDouble(token);  // 尝试将当前 token 转换为数字
            return true;  // 如果转换成功，说明是数字
        } catch (NumberFormatException e) {
            return false;  // 如果发生异常，说明不是数字
        }
    }

    // 判断当前 token 是否为操作符
    private boolean isOperator(String token) {
        return token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/") || token.equals("^");
    }

    // 判断两个操作符的优先级
    private boolean hasPrecedence(String op1, String op2) {
        if (op2.equals("(") || op2.equals(")")) return false;
        if (op1.equals("^") && !op2.equals("^")) return false;
        if ((op1.equals("*") || op1.equals("/")) && (op2.equals("+") || op2.equals("-"))) {
            return false;
        }
        return true;
    }
}
